package indi.mozping.echo.server;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * @author by mozping
 * @Classname EchoServerHandler
 * @Description EchoServerHandler用于处理服务端的逻辑
 * 使用ChannelHandler 的方式体现了关注点分离的设计原则，并简化业务逻辑的迭代开发要求，处理程序只需要覆盖对应的钩子方法，这些钩子方法
 * 对应了活动的周期的适当的时机，比如覆盖channelRead是因为我们需要读取数据
 * <p>
 * 覆盖exceptionCaught让我们应对程序出现Throwable，此时记录异常，并关闭可能处于异常状态的连接，每一个Channel都有一个关联的ChannelPipeline，它代表了
 * ChannelHandler实例的链。适配器处理的实现只是将一个处理方法调用转发到链中的下一个处理器。因此，如果一个 Netty 应用程序不覆盖exceptionCaught ，那么
 * 这些错误将最终到达 ChannelPipeline，并且结束警告将被记录。出于这个原因，你应该提供至少一个实现exceptionCaught的ChannelHandler。
 * <p>
 * 关键点要牢记：
 * ChannelHandler 是给不同类型的事件调用
 * 应用程序实现或扩展 ChannelHandler 挂接到事件生命周期和提供自定义应用逻辑。
 * @Date 2019/9/11 19:26
 * <p>
 * Sharable标识这类的实例之间可以在 channel 里面共享
 */
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    public EchoServerHandler() {
        System.out.println("EchoServerHandler 构造方法执行 ...");
    }

    /**
     * 读取到消息之后的处理逻辑
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx,
                            Object msg) {
        ByteBuf in = (ByteBuf) msg;
        //1.日志消息输出到控制台
        System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
        //2.将所接收的消息返回给发送者。注意，这还没有flush数据
        ctx.write(in);
    }

    /**
     * 读取到消息之后的处理逻辑
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //冲刷所有待审消息到远程节点。关闭通道后，操作完成
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

    /**
     * 发生异常之后的处理逻辑
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        //关闭通道
        ctx.close();
    }
}